﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// /
    /// </summary>
    public class HistogramNormalization : BaseImageFilter
    {
        /// <summary>
        /// 
        /// </summary>
        public HistogramNormalization()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_hstogramOperation"></param>
        internal HistogramNormalization(HistogramNormalization _hstogramOperation)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(LowLevelGraphics.UnsafeBitmap image)
        {
            unsafe
            {
                Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
                int pixelSize = (image.PixelFormat == PixelFormat.Format8bppIndexed) ? 1 :
                    (image.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;


                int startX = rect.Left;
                int startY = rect.Top;
                int stopX = startX + rect.Width;
                int stopY = startY + rect.Height;
                int numberOfPixels = (stopX - startX) * (stopY - startY);
                int stride = image.BitmapData.Stride;
                int offset = stride - rect.Width * pixelSize;            // color image
                byte* ptr = (byte*)image.BitmapData.Scan0;
                // allign pointer to the first pixel to process
                ptr += (startY * stride + startX * pixelSize);

                // calculate histogram
                int[] histogramR = new int[256];
                int[] histogramG = new int[256];
                int[] histogramB = new int[256];

                for (int y = startY; y < stopY; y++)
                {
                    for (int x = startX; x < stopX; x++, ptr += pixelSize)
                    {
                        histogramR[ptr[(short)2]]++; 
                        histogramG[ptr[(short)1]]++; 
                        histogramB[ptr[(short)0]]++;
                    }
                    ptr += offset;
                }

                // calculate new intensity levels
                byte[] equalizedHistogramR = Equalize(histogramR, numberOfPixels);
                byte[] equalizedHistogramG = Equalize(histogramG, numberOfPixels);
                byte[] equalizedHistogramB = Equalize(histogramB, numberOfPixels);

                // update pixels' intensities
                ptr = (byte*)image.BitmapData.Scan0;
                // allign pointer to the first pixel to process
                ptr += (startY * stride + startX * pixelSize);

                for (int y = startY; y < stopY; y++)
                {
                    for (int x = startX; x < stopX; x++, ptr += pixelSize)
                    {
                        ptr[(short)2] = equalizedHistogramR[ptr[(short)2]];
                        ptr[(short)1] = equalizedHistogramG[ptr[(short)1]];
                        ptr[(short)0] = equalizedHistogramB[ptr[(short)0]];
                    }
                    ptr += offset;
                }
            }
            return image;
        }

        // Histogram 
        private byte[] Equalize(int[] histogram, long numPixel)
        {
            byte[] equalizedHistogram = new byte[256];
            float coef = 255.0f / numPixel;

            // calculate the first value
            float prev = histogram[0] * coef;
            equalizedHistogram[0] = (byte)prev;

            // calcualte the rest of values
            for (int i = 1; i < 256; i++)
            {
                prev += histogram[i] * coef;
                equalizedHistogram[i] = (byte)prev;
            }

            return equalizedHistogram;
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HistogramNormalization(this);
        }
    }
}
